package com.mstar.qs.common;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

import net.sf.json.JSONObject;
import net.sf.json.JsonConfig;
import net.sf.json.util.CycleDetectionStrategy;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;

public class BeanConverter {

	static {
		ConvertUtils
				.register(new UtilDateConverter(null), java.util.Date.class);
		ConvertUtils.register(new SqlDateConverter(null), java.sql.Date.class);
		ConvertUtils.register(new SqlTimestampConverter(null),
				java.sql.Timestamp.class);

	}

	public static <T, V> List<V> convert(List<T> orgi, Class<V> clasz) {
		List<V> dest = new ArrayList<V>();
		for (Object o : orgi) {
			try {
				dest.add(BeanConverter.convert(clasz, o));
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return dest;
	}

	public static <T, V> V convert(Class<V> clasz, T orig)
			throws InstantiationException, IllegalAccessException,
			InvocationTargetException {
		V o = null;
		o = (V) clasz.newInstance();
		BeanUtils.copyProperties(o, orig);
		return o;
	}

	public static <T, V> V convert(V dst, T orig)
			throws InstantiationException, IllegalAccessException,
			InvocationTargetException, DefQsException {
		try {
			BeanUtils.copyProperties(dst, orig);
		} catch (Throwable e) {
			throw new DefQsException(e);
		}
		return dst;
	}

	public static String generatePrintableString(Object bean, String separator,
			String[] exceptedProperties) {
		Set<String> properties = null;
		if (exceptedProperties != null && exceptedProperties.length > 0) {
			properties = new HashSet<String>();
			for (String pro : exceptedProperties) {
				properties.add(pro);
			}
		}
		return generatePrintableString(bean, properties, separator);
	}

	@SuppressWarnings("unchecked")
	private static String generatePrintableString(Object bean,
			Set<String> exceptedProperties, String separator) {
		StringBuilder sb = new StringBuilder();
		Class c = bean.getClass();
		for (; c != null;) {

			Field[] fields = c.getDeclaredFields();

			// sb.append(String.format(fmt,
			// "Class name",bean.getClass().getName())).append(separator);
			// System.out.println(bean.getClass().getName());
			for (Field f : fields) {
				int m = f.getModifiers();
				if (Modifier.isStatic(m))
					continue;
				String fieldName = f.getName();
				if (exceptedProperties != null
						&& exceptedProperties.contains(fieldName))
					continue;
				try {
					PropertyDescriptor pd = new PropertyDescriptor(fieldName,
							bean.getClass());
					Method method = pd.getReadMethod();
					Object value = method.invoke(bean, new Object[0]);
					String str = String.format(fmt, fieldName,
							(value == null ? "" : value.toString()));
					// System.out.println(str);
					sb.append(str);
					if (separator == null) {
						sb.append(DEFAULT_SEPARATOR);
					} else {
						sb.append(separator);
					}
				} catch (IntrospectionException e) {
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					e.printStackTrace();
				}

			}
			c = c.getSuperclass();
		}

		return sb.toString();
	}

	public static String convertPropertiesToString(Object entityName) {
		return convertPropertiesToString(entityName, false);
	}

	/**
	 * Convert properties which has complied with POJIO specification to text
	 * String
	 * 
	 * @param entityName
	 *            object that wanted to be converted text string.
	 * 
	 * @param splitProperties
	 *            each propertiy following an line seperator if true.
	 * @return
	 */
	public static String convertPropertiesToString(Object entityName,
			boolean splitProperties) {
		
		JsonConfig jsonConfig=new JsonConfig();  
		jsonConfig.setCycleDetectionStrategy(CycleDetectionStrategy.LENIENT);  
		return JSONObject.fromObject(entityName, jsonConfig).toString();
	}
	
	
/**
 * 
 * 
 * @param entityName
 * @return
 * 
 * 
 * <code>
 * 
		Class c = entityName.getClass();
		Field fields[] = c.getDeclaredFields();
		StringBuffer sb = new StringBuffer();
		Method[] ms = c.getMethods();
		Set<String> mms = new HashSet<String>();
		for (Method m : ms) {
			int mod = m.getModifiers();
			String name = m.getName();
			if (Modifier.isPublic(mod)
					&& (name.startsWith("get") || name.startsWith("is"))
					&& m.getParameterTypes().length == 0)
				mms.add(m.getName());
		}

		sb.append("{");
		for (Field f : fields) {
			int m = f.getModifiers();
			if (Modifier.isStatic(m))
				continue;
			String fieldName = f.getName();
			fieldName = fieldName.substring(0, 1).toUpperCase()
					+ fieldName.substring(1);
			String fn = "get" + fieldName;
			// LogFactory.COMMON_LOGGER.info(fn);
			if (!mms.contains(fn)) {
				fn = "is" + fieldName;
				if (!mms.contains(fn)) {
					continue;
				}
			}

			sb.append(fieldName);
			sb.append(" : ");
			sb.append(invokeMethod(entityName, fn));
			if (splitProperties)
				sb.append(GlobalConfig.LINE_SEPARATOR);
			else {
				sb.append(",");
			}

		}
		sb.append("}");
		return sb.toString();
 * </code>
 */
	public static String convertPropertiesToXml(Object entityName) {

		JAXBContext jaxbContext;
		try {
			jaxbContext = JAXBContext.newInstance(entityName.getClass());
			Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

			// output pretty printed
			jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

			// jaxbMarshaller.marshal(p, file);
			// jaxbMarshaller.marshal(p, System.out);
			// ResultTemp r=new ResultTemp();
			ByteArrayOutputStream o = new ByteArrayOutputStream();
			jaxbMarshaller.marshal(entityName, o);
			return o.toString();
		} catch (JAXBException e) {
			e.printStackTrace();
		}
		return null;
	}

	private static Object invokeMethod(Object owner, String methodName) {
		Class ownerClass = owner.getClass();

		Method method = null;

		try {
			method = ownerClass.getMethod(methodName);
			return method.invoke(owner);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return "Failed to retrieve data with method: " + methodName;

	}

	private static final String fmt = "%20s = %s";
	private static final String DEFAULT_SEPARATOR = ", ";
}
